package searchprocessing.exactmatch;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.lucene.queryparser.classic.ParseException;

import database.Solr_sim;
import structure.AssetNode;
import structure.EdgeMatchpair;
import structure.NodeMatchpair;
import structure.Relationship;
/**
 * 程式資訊摘要 : 比對單元
 * 版本資訊:2.0
 * 程式設計人員姓名: 朱聖池
 */
abstract public class MatchedUnit {
	//Query之node and edge
	protected HashMap<String, AssetNode> graph_Node = new HashMap<String, AssetNode>();
	protected ArrayList<Relationship> graph_Edge = new ArrayList<Relationship>();
	private HashMap<String, ArrayList<Relationship>> headNode_Edge_list = new HashMap<String, ArrayList<Relationship>>();
	private float nodeThreshold = (float) 0.5 ;
	private float edgeThreshold = (float) 0.5;
	private double lambda = 0.5 , alpha = 0.5 , beta = 0.5;
	protected ArrayList<EdgeMatchpair> edgeMatch = new ArrayList<EdgeMatchpair>() ;
	protected HashMap<String,Double> queryMatch = new HashMap<String,Double>();
	protected ArrayList<NodeMatchpair> nodeMatch= new ArrayList<NodeMatchpair>() ;
	protected HashMap<String,ArrayList<EdgeMatchpair>> solution_EdgeMatch = new HashMap<String,ArrayList<EdgeMatchpair>>();
	protected HashMap<String,ArrayList<NodeMatchpair>> solution_NodeMatch = new HashMap<String,ArrayList<NodeMatchpair>>();
	protected int checkEdgetype = 0 ;
	protected HashMap<String,String> convertName = new HashMap<String,String>();

	/**
	 * 從資料分析出來，擷取node到此class做比對
	 */
	public void setNode(HashMap<String, AssetNode> query_node){
		this.graph_Node = query_node;
	}
	
	/**
	 * 從資料分析出來，擷取edge到此class做比對
	 * @param query_edge
	 */
	public void setEdge(ArrayList<Relationship> query_edge){
		this.graph_Edge = query_edge;
	}
	
	/**
	 * 設定智產點的門檻值
	 * @param node
	 */
	public void setNodeThreshold(float node){
		this.nodeThreshold = node ;
	}
	
	/**
	 * 設定Lambda參數權重
	 * @param weight
	 */
	public void setLambda(double weight){
		this.lambda = weight ;
	}
	
	/**
	 * 設定alpha參數權重
	 * @param weight
	 */
	public void setAlpha(double weight){
		this.alpha = weight ;
	}
	
	/**
	 * 設定beta參數權重
	 * @param weight
	 */
	public void setBeta(double weight){
		this.beta = weight ;
	}
	
	/**
	 * 回傳alpha參數權重
	 * @return  double
	 */
	public double getAlpha(){
		return this.alpha;
	}
	
	/**
	 * 回傳beta參數權重
	 * @return  double
	 */
	public double getBeta(){
		return this.beta ;
	}
	
	/**
	 * 改變關係的比對模式 0=完全關係 1=相似關係 2=任意關係
	 * @param edgetype
	 */
	public void setEdgeMatchType(int edgetype){
		this.checkEdgetype = edgetype ;
	}
	
	/**
	 * 回傳當前關係的模式
	 * @return int
	 */
	public int getEdgeMatchType(){
		return this.checkEdgetype;
	}
	
	/**
	 * 得到query對上data的相似分數table
	 * @return HashMap<String,Double> queryMatch
	 */
	public HashMap<String,Double> getQueryMatchTable(){
		return queryMatch;
	}
	
	/**
	 * key = solutionid_version value=匹配邊的list
	 * @return HashMap<String,ArrayList<EdgeMatchpair>> solution_EdgeMatch
	 */
	public HashMap<String,ArrayList<EdgeMatchpair>> getSolution_EdgeMatchTable(){
		return solution_EdgeMatch;
	}
	
	/**
	 * key = solutionid_version value=匹配點的list
	 * @return solution_NodeMatch
	 */
	public HashMap<String,ArrayList<NodeMatchpair>> getSolution_NodeMatchTable(){
		return solution_NodeMatch;
	}
	
	/**
	 * 給予solutionid+"_"+version 會給匹配邊的list及分數
	 * @param solutionid_version
	 * @return ArrayList<EdgeMatchpair>
	 */
	public ArrayList<EdgeMatchpair> get_EdgeMatch(String solutionid_version){
		return solution_EdgeMatch.get(solutionid_version);
	}
	
	/**
	 * 給予solutionid+"_"+version 會給匹配點的list及分數
	 * @param solutionid_version
	 * @return ArrayList<NodeMatchpair>
	 */
	public ArrayList<NodeMatchpair> get_NodeMatch(String solutionid_version){
		return solution_NodeMatch.get(solutionid_version);
	}
	
	
	/**
	 * 將query的雙向邊統一正規化成單邊(由上至下)
	 * @param edge
	 * @return 單向的特徵邊list
	 */
	public ArrayList<Relationship> direction_normalize(ArrayList<Relationship> edge) {
		ArrayList<Relationship> direction_edge = new ArrayList<Relationship>();
		for(int index=0;index<edge.size();index++){
			if(edge.get(index).getHead().getAssetLayerType()<edge.get(index).getTail().getAssetLayerType()){
				direction_edge.add(edge.get(index));
			}
			else if(edge.get(index).getHead().getAssetLayerType()==edge.get(index).getTail().getAssetLayerType()){
				String relation = edge.get(index).getRelations();
				if(relation.equals("Realize") || relation.equals("Associate with") || 
						relation.equals("Compose") || relation.equals("Aggregate")){
					direction_edge.add(edge.get(index));
				}
			}
		}
		return direction_edge;
	}
	
	/**
	 * 檢查候選集的組合是否存在於當前的Data裡面
	 * @param headnode
	 * @param tailnode
	 * @param gdbEdge
	 * @param gdbNode 
	 * @param flag 
	 * @return ArrayList<Relationship>
	 */
	public ArrayList<Relationship> checkEdge(Entry<String, Double> headnode,
			Entry<String, Double> tailnode , ArrayList<Relationship> gdbEdge, HashMap<String, AssetNode> gdbNode, boolean flag) {
	   
		ArrayList<Relationship> simEdge = new  ArrayList<Relationship>();
		//System.out.println("目前代換邊為:"+gdbNode.get(headnode.getKey()).getAssetName()+","+gdbNode.get(tailnode.getKey()).getAssetName());
		if(headnode.getKey().equals(tailnode.getKey())) return simEdge;
		
		for(Relationship DataEdge : gdbEdge){
			String hid = DataEdge.getHead().getAssetID()+"_"+DataEdge.getHead().getVersion();
			String tid = DataEdge.getTail().getAssetID()+"_"+DataEdge.getTail().getVersion();
			//將當前的候選點 跟 DataEdge哪條完全匹配
			//test
			//System.out.println("目前搜尋到的候選邊:"+DataEdge.getHead().getAssetName()+","+DataEdge.getTail().getAssetName());
			if(hid.equals(headnode.getKey()) && DataEdge.getHead().getAssetLayerType()
					==gdbNode.get(headnode.getKey()) .getAssetLayerType()
					&& tid.equals(tailnode.getKey()) && DataEdge.getTail().getAssetLayerType()
					==gdbNode.get(tailnode.getKey()) .getAssetLayerType()){
				
				simEdge.add(DataEdge);
				
			}
			//有可能A-r-B  但對應不到B- rb -A這兩條是一樣的
			if(flag){
				if(hid.equals(tailnode.getKey()) && DataEdge.getTail().getAssetLayerType()
						==gdbNode.get(headnode.getKey()) .getAssetLayerType()
						&& tid.equals(headnode.getKey()) && DataEdge.getHead().getAssetLayerType()
						==gdbNode.get(tailnode.getKey()) .getAssetLayerType()){
					Boolean isExist = false;
					Relationship reverseEdge = new Relationship();
					
					reverseEdge.setHead(DataEdge.getTail());
					reverseEdge.setTail(DataEdge.getHead());
					
					if(DataEdge.getRelations().equals("Realize")){
						reverseEdge.setRelations("Realized by");
					}
					else if(DataEdge.getRelations().equals("Realized by")){
						reverseEdge.setRelations("Realize");
					}
					else if(DataEdge.getRelations().equals("Compose")){
						reverseEdge.setRelations("Compose by");
					}
					else if(DataEdge.getRelations().equals("Composed by")){
						reverseEdge.setRelations("Compose");
					}
					else if(DataEdge.getRelations().equals("Associate with")){
						reverseEdge.setRelations("Associated by");
					}
					else if(DataEdge.getRelations().equals("Associated by")){
						reverseEdge.setRelations("Associate with");
					}
					else if(DataEdge.getRelations().equals("Aggregate")){
						reverseEdge.setRelations("Aggregated by");
					}
					else if(DataEdge.getRelations().equals("Aggregated by")){
						reverseEdge.setRelations("Aggregate");
					}
					

					simEdge.add(reverseEdge);
					
				}
			}
			

		}
		return simEdge ;
	}
	
	/**
	 * 檢查邊上的關係容許完全，模糊，不考慮 
	 * @param edge_candidate
	 * @param relations
	 * @return HashMap<Relationship , Double>
	 */
	abstract protected HashMap<Relationship , Double> equalship(ArrayList<Relationship> edge_candidate, String relations);
	/**
	 * 任意關係
	 * @param edge_candidate
	 * @param string
	 * @return boolean
	 */
	private boolean anyship(String check, String relations) {
		if(!check.equals(relations)){
			if(!(check.contains(relations) || relations.contains(check))){
				return true ;
			}
		}
		return false ;
	}
	/**
	 * 關係同屬性即可
	 * @param check
	 * @param relations
	 * @return boolean
	 */
	public boolean simship(String check, String relations) {
		if(!check.equals(relations)){
			if(check.contains(relations) || relations.contains(check)){
				return true ;
			}
		}
		return false;
	}
	/**
	 * 
	 * @param check
	 * @param relations
	 * @return boolean
	 */
	protected boolean exactship(String check, String relations) {
		if(check.equals(relations)){
			return true ;
		}
		return false;
	}
	
	/**
	 * 選出最大分數的組合匹配邊
	 * @param querytodata
	 * @return EdgeMatchpair
	 */
	protected EdgeMatchpair maxScoreEdgeMatched(ArrayList<EdgeMatchpair> querytodata) {
		double maxScore = 0.0 ;
		double initCom = 0.0 ;
		EdgeMatchpair maxSelected = null ;
		
		for(EdgeMatchpair selectEdge : querytodata){
			maxSelected = new EdgeMatchpair() ;
			initCom = selectEdge.getSimilarEdgeScore();
			
			if(initCom > maxScore && maxSelected!=null){
				maxScore = initCom ;
				maxSelected = selectEdge ;
			}
			else{
				maxScore = initCom ;
				maxSelected = selectEdge ;
			}
		}
		return maxSelected ;
	}
	
	/**
	 * 選出queyEdge最高分的匹配邊 dataEdge
	 * @param edgeshipScore
	 * @param retrival_list_head
	 * @param retrival_list_tail
	 * @return EdgeMatchpair
	 */
	protected EdgeMatchpair selectScoreMaxEdge(
			HashMap<Relationship, Double> edgeshipScore,
			HashMap<String, Double> retrival_list_head,
			HashMap<String, Double> retrival_list_tail) {
		
		double maxScore = 0.0 ;
		double initCom = 0.0 ;
		Relationship maxSelected = null ;
		
		for(Map.Entry<Relationship, Double> selectEdge : edgeshipScore.entrySet()){
			
			double uphead = appendAtrribute(selectEdge.getKey().getHead() , 
					retrival_list_head.get(selectEdge.getKey().getHead().getAssetID()+"_"+
					selectEdge.getKey().getHead().getVersion())) ;
			//uphead = retrival_list_head.get(selectEdge.getKey().getHead().getAssetID()+"_"+selectEdge.getKey().getHead().getVersion()) ;
			
			double uptail = appendAtrribute(selectEdge.getKey().getTail() , 
					retrival_list_tail.get(selectEdge.getKey().getTail().getAssetID()+"_"+
					selectEdge.getKey().getTail().getVersion())) ;
			//uptail = retrival_list_tail.get(selectEdge.getKey().getTail().getAssetID()+"_"+selectEdge.getKey().getTail().getVersion()) ;

			initCom = selectEdge.getValue() * (lambda * uphead + (1-lambda) * uptail)  ;
			
			if(initCom > maxScore && initCom > 0){
				maxScore = initCom ;
				maxSelected = selectEdge.getKey() ;
			}
					
		}
		
		EdgeMatchpair set_datapair = null ;
		if(maxSelected!=null){
			set_datapair = new EdgeMatchpair();
			set_datapair.setDataEdge(maxSelected);
			set_datapair.setSimilarEdgeScore(maxScore);
		}
		return set_datapair ;
	}
	
	/**
	 * 移除已匹配好的data邊
	 * @param dbedge
	 * @param matchedEdge
	 */
	protected ArrayList<Relationship> rmdbedge(ArrayList<Relationship> dbedge,
			EdgeMatchpair matchedEdge) {
		
		for(int i=0;i<dbedge.size();i++){
			if(dbedge.get(i).equals(matchedEdge.getDataEdge())){
				dbedge.remove(i);
				break;
			}
		}
		return dbedge;
	}
	
	/**
	 * 從當前資料庫，找出Graph結構Matched
	 * @throws ParseException 
	 * @throws IOException 
	 * 
	 */
	abstract public void matched() throws ParseException, IOException;
	
	/**
	 * 排序HashMap分數
	 * @param unsortMap
	 */
	public void mapSort(HashMap<String, Double> unsortMap){
		//sort
		List<Entry<String, Double>> final_Rank =
	            new ArrayList<Map.Entry<String, Double>>(unsortMap.entrySet());
	        //排序mapsort依分數大小  
        Collections.sort(final_Rank, new Comparator<Map.Entry<String, Double>>(){
        	public int compare(Map.Entry<String, Double> o1, Map.Entry<String, Double> o2)
            {
              if ((o2.getValue() - o1.getValue())>0)
                return 1;
              else if((o2.getValue() - o1.getValue())==0)
                return 0;
              else 
                return -1;
            }
        });
		
        for(int finalsize=0;finalsize<final_Rank.size();finalsize++){
        	System.out.println(convertName.get(final_Rank.get(finalsize).getKey())
        			+"  "+queryMatch.get(final_Rank.get(finalsize).getKey())) ;
        	//System.out.println(final_Rank.get(finalsize).getKey()+" : "+queryMatch.get(final_Rank.get(finalsize).getKey())) ;
        }
	}
	/**
	 * 儲存以某點為開頭的邊集合
	 * @param gdbEdge
	 */
	private void getData_HeadMap(ArrayList<Relationship> gdbEdge) {
		HashMap<String, ArrayList<Relationship>> data_headedge = new HashMap<String, ArrayList<Relationship>>();
		for(int edgenum=0;edgenum<gdbEdge.size();edgenum++){
			ArrayList<Relationship> headguild = new ArrayList<Relationship>();
			String key = gdbEdge.get(edgenum).getHead().getAssetID()+"_"+gdbEdge.get(edgenum).getHead().getVersion();
			if(!data_headedge.containsKey(key)){
				headguild.add(gdbEdge.get(edgenum));
				data_headedge.put(key, headguild);
			}
			else{
				ArrayList<Relationship> addlist = data_headedge.get(key);
				addlist.add(gdbEdge.get(edgenum));
				data_headedge.put(key, addlist);
			}
		}
		this.headNode_Edge_list = data_headedge;
		
	}
	/**
	 * 計算整體分數(每邊的相似度相乘)
	 * @param edgeMatch2
	 * @return double型態回傳整體分數
	 */
	protected Double calculateAllScore(ArrayList<EdgeMatchpair> edgeMatch) {
		int loseEdge = graph_Edge.size() - edgeMatch.size() ;
		double maxEdgeScore = 0.0;
		double structure_score = 0;
		double TotalScore = 0.0 ; // 屬性分數統計

		//平均分數
		if(loseEdge>0){
			double similarScore = 0.0;
			for(int index=0;index<edgeMatch.size();index++){
				similarScore += edgeMatch.get(index).getSimilarEdgeScore();
			}
			TotalScore = similarScore/graph_Edge.size() ;
		}
		else if(loseEdge == 0){
			double similarScore = 0.0;
			for(int index=0;index<edgeMatch.size();index++){
				similarScore+=edgeMatch.get(index).getSimilarEdgeScore();
			}
			TotalScore = 1.0*similarScore/edgeMatch.size() ;
		}
		
		return TotalScore ;
	}
	
	/**
	 * 更新邊的資訊
	 * @param gdbNode
	 * @param gdbEdge
	 * @return ArrayList<Relationship>
	 */
	private ArrayList<Relationship> updateEdge(HashMap<String, AssetNode> gdbNode, ArrayList<Relationship> gdbEdge) {
		ArrayList<Relationship> D_Edge = new ArrayList<Relationship>();
		for(Relationship D_edge : gdbEdge){
			D_edge.setHead(gdbNode.get(D_edge.getHead().getAssetID()+"_"+D_edge.getHead().getVersion())) ;
			D_edge.setTail(gdbNode.get(D_edge.getTail().getAssetID()+"_"+D_edge.getTail().getVersion())) ;
			D_Edge.add(D_edge);
		}
		return D_Edge;
	}
	
	/**
	 * 將匹配好的結構邊加入屬性solr分數
	 * @param edgepair
	 */
    private ArrayList<EdgeMatchpair> appendAtrribute(ArrayList<EdgeMatchpair> edgepair) {
    	ArrayList<EdgeMatchpair> pairappend = new ArrayList<EdgeMatchpair>();
    	Solr_sim webSearch = new Solr_sim();
    	HashMap<String, HashMap<String, String>> retrival_list ;
		for(int pair_count=0;pair_count<edgepair.size();pair_count++){
			EdgeMatchpair indexedge = edgepair.get(pair_count);
			String query_name = indexedge.getQueryEdge().getHead().getAssetName();
			String data_id = indexedge.getDataEdge().getHead().getAssetID()+"_"+
					indexedge.getDataEdge().getHead().getVersion();
			retrival_list = webSearch.getSimilarList(query_name);
			if(retrival_list.containsKey(data_id)){
				double attribute_score = Double.parseDouble(retrival_list.get(data_id).get("Score"));
				if(attribute_score > indexedge.getSimilarEdgeScore()){
					indexedge.setSimilarEdgeScore(attribute_score);
				}
			}
			pairappend.add(indexedge);
		}
		return pairappend;
		
	}
    
    /**
     * 取得正規化後的Query Edge
     * @return ArrayList<Relationship>
     */
	public ArrayList<Relationship> getEdge() {
		// TODO Auto-generated method stub
		return this.graph_Edge;
	}
	
	/**
     * 使用solr查詢屬性的分數
     * @param queryNode
     * @return double
     */
    private double appendAtrribute(AssetNode queryNode , double structureScore) {
    	Solr_sim webSearch = new Solr_sim();
    	String query_name = queryNode.getAssetName();
    	String data_id = queryNode.getAssetID()+"_"+queryNode.getVersion();
    	
    	HashMap<String, HashMap<String, String>> retrival_list = webSearch.getSimilarList(query_name);
    	
		if(retrival_list.containsKey(data_id)){
			double attribute_score = Double.parseDouble(retrival_list.get(data_id).get("Score"));
			if(attribute_score > 0){
				return (lambda*structureScore+(1-lambda)*attribute_score) ;
			}
		}
    	
		return structureScore; //*lambda
    }
    
	/**
	 * 取得正規化後的data Edge
	 * @return HashMap<String, AssetNode>
	 */
	public HashMap<String, AssetNode> getNode() {
		// TODO Auto-generated method stub
		return this.graph_Node;
	}
    
    /**
     * 回傳名稱比對表
     * @return HashMap<String, String>
     */
    public HashMap<String, String> getConvertName(){
    	return this.convertName;
    }
}
